#include <TFT_eSPI.h>
#include "lvgl.h"
#include "demos/lv_demos.h"
#include <Arduino.h>
#include <Wire.h>
#include <Preferences.h>
#include <Ticker.h>
#include <CST816S.h>
#include "SHTSensor.h"
#include "SparkFun_SGP30_Arduino_Library.h"

// 初始化传感器和触摸屏
SHTSensor sht(SHTSensor::SHT3X);
SGP30 sgp;
CST816S touch(5, 4, 2, 7);

float sht_temp = 0;
float sht_hum = 0;

// 屏幕分辨率
static const uint16_t screenWidth = 240;
static const uint16_t screenHeight = 240;

// 引脚配置
static const uint8_t BEE_PIN = 10;
static const uint8_t FAN_PIN = 1;

// 显示和绘图缓冲区
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * screenHeight / 10];
TFT_eSPI tft = TFT_eSPI(screenWidth, screenHeight);

// 存储偏好
Preferences preferences;

Ticker bl_ticker;
Ticker update_chart_ticker;

// 字体和样式
static lv_obj_t *tv;
static const lv_font_t *font_large;
static const lv_font_t *font_title;
static const lv_font_t *font_normal;

static lv_style_t style_icon;
static lv_style_t style_text_muted;
static lv_style_t style_title;
static lv_style_t style_text;
static lv_style_t style_bullet;
static lv_style_t style_column_small;

static lv_obj_t *chart_tvoc;
static lv_chart_series_t *ser1_tvoc;
static lv_obj_t *chart_co2;
static lv_chart_series_t *ser1_co2;
static lv_obj_t *chart_temp;
static lv_chart_series_t *ser1_temp;
static lv_obj_t *chart_hum;
static lv_chart_series_t *ser1_hum;

// 显示刷新回调
void my_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors((uint16_t *)&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp_drv);
}

// 触摸屏读取回调
void my_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) {
    if (!touch.available()) {
        data->state = LV_INDEV_STATE_REL;
    } else {
        data->state = LV_INDEV_STATE_PR;
        data->point.x = touch.data.y;
        data->point.y = screenWidth - touch.data.x;
    }
}

// 更新传感器数据
void update_tvoc_text(lv_timer_t *timer) {
    lv_obj_t *label = (lv_obj_t *)timer->user_data;
    float temp, humi, absoluteHumidity;

    if (sht.readSample()) {
        temp = sht.getTemperature();
        humi = sht.getHumidity();
        absoluteHumidity = humi * 6.112 * exp((17.67 * temp) / (temp + 243.5)) / ((273.15 + temp) * 2.1674);
    }
    sgp.setHumidity(absoluteHumidity);
    sgp.measureAirQuality();

    lv_label_set_text_fmt(label, "TVOC:  %d ppb", sgp.TVOC);
}

void update_co2_text(lv_timer_t *timer) {
    lv_obj_t *label = (lv_obj_t *)timer->user_data;

    sgp.measureAirQuality();
    lv_label_set_text_fmt(label, "CO2:  %d ppm", sgp.CO2);
}

void update_temp_text(lv_timer_t *timer) {
    lv_obj_t *label = (lv_obj_t *)timer->user_data;

    if (sht.readSample()) {
      sht_temp  = sht.getTemperature();
      sht_hum = sht.getHumidity();
      lv_label_set_text_fmt(label, "%.1f °C | %.1f %%", sht_temp, sht_hum);
    }
}

// 创建Home界面
static void home_create(lv_obj_t *parent) {
    lv_obj_t *tvoc_obj = lv_obj_create(parent);
    lv_obj_set_height(tvoc_obj, LV_SIZE_CONTENT);
    
    lv_obj_t *tvoc_label = lv_label_create(tvoc_obj);
    lv_label_set_text_fmt(tvoc_label, "TVOS:  -- ppb");
    lv_obj_add_style(tvoc_label, &style_title, 0);

    lv_obj_t *tvoc_icn = lv_label_create(tvoc_obj);
    lv_obj_add_style(tvoc_icn, &style_icon, 0);
    lv_label_set_text(tvoc_icn, LV_SYMBOL_WARNING);

    lv_obj_t *co2_obj = lv_obj_create(parent);
    lv_obj_set_height(co2_obj, LV_SIZE_CONTENT);

    lv_obj_t *co2_label = lv_label_create(co2_obj);
    lv_label_set_text_fmt(co2_label, "CO2:  -- ppm");
    lv_obj_add_style(co2_label, &style_title, 0);

    lv_obj_t *co2_icn = lv_label_create(co2_obj);
    lv_obj_add_style(co2_icn, &style_icon, 0);
    lv_label_set_text(co2_icn, LV_SYMBOL_WARNING);

    lv_obj_t *temperature_obj = lv_obj_create(parent);
    lv_obj_set_height(temperature_obj, LV_SIZE_CONTENT);

    lv_obj_t *temperature_label = lv_label_create(temperature_obj);
    lv_label_set_text_fmt(temperature_label, "-- °C  -- %%");
    lv_obj_add_style(temperature_label, &style_title, 0);

    lv_obj_t *temp_icn = lv_label_create(temperature_obj);
    lv_obj_add_style(temp_icn, &style_icon, 0);
    lv_label_set_text(temp_icn, LV_SYMBOL_OK);

    static lv_coord_t grid_main_col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t grid_main_row_dsc[] = {LV_GRID_CONTENT, 
                                            LV_GRID_CONTENT, 
                                            LV_GRID_CONTENT, 
                                            LV_GRID_CONTENT, 
                                            LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(parent, grid_main_col_dsc, grid_main_row_dsc);


    static lv_coord_t grid_tvoc_col_dsc[] = {LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t grid_tvos_row_dsc[] = {LV_GRID_CONTENT, /*info*/
                                             LV_GRID_CONTENT, /*history*/
                                             LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(tvoc_obj, grid_tvoc_col_dsc, grid_tvos_row_dsc);

    static lv_coord_t grid_co2_col_dsc[] = {LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t grid_co2_row_dsc[] = {LV_GRID_CONTENT, /*info*/
                                             LV_GRID_CONTENT, /*history*/
                                             LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(co2_obj, grid_co2_col_dsc, grid_co2_row_dsc);

    static lv_coord_t grid_temp_col_dsc[] = {LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t grid_temp_row_dsc[] = {LV_GRID_CONTENT, /*info*/
                                             LV_GRID_CONTENT, /*history*/
                                             LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(temperature_obj, grid_temp_col_dsc, grid_temp_row_dsc);

    lv_obj_set_grid_cell(tvoc_obj, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(tvoc_icn, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(tvoc_label, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);

    lv_obj_set_grid_cell(co2_obj, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 1, 1);
    lv_obj_set_grid_cell(co2_icn, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(co2_label, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);

    lv_obj_set_grid_cell(temperature_obj, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 2, 1);
    lv_obj_set_grid_cell(temp_icn, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(temperature_label, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);

    // 创建定时器以更新传感器数据
    lv_timer_t *up_tvoc_timer = lv_timer_create(update_tvoc_text, 1010, tvoc_label);
    lv_timer_t *up_co2_timer = lv_timer_create(update_co2_text, 1005, co2_label);
    lv_timer_t *up_temp_timer = lv_timer_create(update_temp_text, 1000, temperature_label);
}

static void chart_tvoc_event_cb(lv_event_t * e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t * obj = lv_event_get_target(e);

    if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) {
        lv_obj_invalidate(obj); /*To make the value boxes visible*/
    }
    else if(code == LV_EVENT_DRAW_PART_BEGIN) {
        lv_obj_draw_part_dsc_t * dsc = (lv_obj_draw_part_dsc_t *)lv_event_get_param(e);
        /*Set the markers' text*/
        if(dsc->part == LV_PART_TICKS && dsc->id == LV_CHART_AXIS_PRIMARY_X) {
            if(lv_chart_get_type(obj) == LV_CHART_TYPE_BAR) {
                const char * month[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"};
                lv_snprintf(dsc->text, dsc->text_length, "%s", month[dsc->value]);
            }
            else {
                const char * month[] = {
                    "0", "2", "4", "6", "8", "10", "12", "14", "16", "18", "20"
                };
                lv_snprintf(dsc->text, dsc->text_length, "%s", month[dsc->value]);
            }
        }

        /*Add the faded area before the lines are drawn */
        else if(dsc->part == LV_PART_ITEMS) {
#if LV_DRAW_COMPLEX
            /*Add  a line mask that keeps the area below the line*/
            if(dsc->p1 && dsc->p2) {
                lv_draw_mask_line_param_t line_mask_param;
                lv_draw_mask_line_points_init(&line_mask_param, dsc->p1->x, dsc->p1->y, dsc->p2->x, dsc->p2->y,
                                              LV_DRAW_MASK_LINE_SIDE_BOTTOM);
                int16_t line_mask_id = lv_draw_mask_add(&line_mask_param, NULL);

                /*Add a fade effect: transparent bottom covering top*/
                lv_coord_t h = lv_obj_get_height(obj);
                lv_draw_mask_fade_param_t fade_mask_param;
                lv_draw_mask_fade_init(&fade_mask_param, &obj->coords, LV_OPA_COVER, obj->coords.y1 + h / 8, LV_OPA_TRANSP,
                                       obj->coords.y2);
                int16_t fade_mask_id = lv_draw_mask_add(&fade_mask_param, NULL);

                /*Draw a rectangle that will be affected by the mask*/
                lv_draw_rect_dsc_t draw_rect_dsc;
                lv_draw_rect_dsc_init(&draw_rect_dsc);
                draw_rect_dsc.bg_opa = LV_OPA_50;
                draw_rect_dsc.bg_color = dsc->line_dsc->color;

                lv_area_t obj_clip_area;
                _lv_area_intersect(&obj_clip_area, dsc->draw_ctx->clip_area, &obj->coords);
                const lv_area_t * clip_area_ori = dsc->draw_ctx->clip_area;
                dsc->draw_ctx->clip_area = &obj_clip_area;
                lv_area_t a;
                a.x1 = dsc->p1->x;
                a.x2 = dsc->p2->x - 1;
                a.y1 = LV_MIN(dsc->p1->y, dsc->p2->y);
                a.y2 = obj->coords.y2;
                lv_draw_rect(dsc->draw_ctx, &draw_rect_dsc, &a);
                dsc->draw_ctx->clip_area = clip_area_ori;
                /*Remove the masks*/
                lv_draw_mask_remove_id(line_mask_id);
                lv_draw_mask_remove_id(fade_mask_id);
            }
#endif


            const lv_chart_series_t * ser = (const lv_chart_series_t *)dsc->sub_part_ptr;

            if(lv_chart_get_pressed_point(obj) == dsc->id) {
                if(lv_chart_get_type(obj) == LV_CHART_TYPE_LINE) {
                    dsc->rect_dsc->outline_color = lv_color_white();
                    dsc->rect_dsc->outline_width = 2;
                }
                else {
                    dsc->rect_dsc->shadow_color = ser->color;
                    dsc->rect_dsc->shadow_width = 15;
                    dsc->rect_dsc->shadow_spread = 0;
                }

                char buf[8];
                lv_snprintf(buf, sizeof(buf), "%" LV_PRIu32, dsc->value);

                lv_point_t text_size;
                lv_txt_get_size(&text_size, buf, font_normal, 0, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);

                lv_area_t txt_area;
                if(lv_chart_get_type(obj) == LV_CHART_TYPE_BAR) {
                    txt_area.y2 = dsc->draw_area->y1 - LV_DPX(15);
                    txt_area.y1 = txt_area.y2 - text_size.y;
                    if(ser == lv_chart_get_series_next(obj, NULL)) {
                        txt_area.x1 = dsc->draw_area->x1 + lv_area_get_width(dsc->draw_area) / 2;
                        txt_area.x2 = txt_area.x1 + text_size.x;
                    }
                    else {
                        txt_area.x2 = dsc->draw_area->x1 + lv_area_get_width(dsc->draw_area) / 2;
                        txt_area.x1 = txt_area.x2 - text_size.x;
                    }
                }
                else {
                    txt_area.x1 = dsc->draw_area->x1 + lv_area_get_width(dsc->draw_area) / 2 - text_size.x / 2;
                    txt_area.x2 = txt_area.x1 + text_size.x;
                    txt_area.y2 = dsc->draw_area->y1 - LV_DPX(15);
                    txt_area.y1 = txt_area.y2 - text_size.y;
                }

                lv_area_t bg_area;
                bg_area.x1 = txt_area.x1 - LV_DPX(8);
                bg_area.x2 = txt_area.x2 + LV_DPX(8);
                bg_area.y1 = txt_area.y1 - LV_DPX(8);
                bg_area.y2 = txt_area.y2 + LV_DPX(8);

                lv_draw_rect_dsc_t rect_dsc;
                lv_draw_rect_dsc_init(&rect_dsc);
                rect_dsc.bg_color = ser->color;
                rect_dsc.radius = LV_DPX(5);
                lv_draw_rect(dsc->draw_ctx, &rect_dsc, &bg_area);

                lv_draw_label_dsc_t label_dsc;
                lv_draw_label_dsc_init(&label_dsc);
                label_dsc.color = lv_color_white();
                label_dsc.font = font_normal;
                lv_draw_label(dsc->draw_ctx, &label_dsc, &txt_area,  buf, NULL);
            }
            else {
                dsc->rect_dsc->outline_width = 0;
                dsc->rect_dsc->shadow_width = 0;
            }
        }
    }
}



// 创建历史记录界面
static void history_create(lv_obj_t *parent) {
    lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_ROW_WRAP);


    static lv_coord_t grid_chart_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_FR(1), 10, LV_GRID_TEMPLATE_LAST};
    static lv_coord_t grid_chart_col_dsc[] = {20, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};

    lv_obj_t *chart_tvoc_obj = lv_obj_create(parent);
    lv_obj_set_flex_grow(chart_tvoc_obj, 1);
    lv_obj_set_grid_dsc_array(chart_tvoc_obj, grid_chart_col_dsc, grid_chart_row_dsc);

    lv_obj_set_height(chart_tvoc_obj, LV_PCT(100));
    lv_obj_set_style_max_height(chart_tvoc_obj, 300, 0);

    lv_obj_t *title_tvoc = lv_label_create(chart_tvoc_obj);
    lv_label_set_text(title_tvoc, "TVOC Data");
    lv_obj_add_style(title_tvoc, &style_title, 0);
    lv_obj_set_grid_cell(title_tvoc, LV_GRID_ALIGN_START, 0, 2, LV_GRID_ALIGN_START, 0, 1);

    chart_tvoc = lv_chart_create(chart_tvoc_obj);
    lv_group_add_obj(lv_group_get_default(), chart_tvoc);
    lv_obj_add_flag(chart_tvoc, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
    lv_obj_set_grid_cell(chart_tvoc, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_STRETCH, 1, 1);
    lv_chart_set_axis_tick(chart_tvoc, LV_CHART_AXIS_PRIMARY_Y, 0, 0, 5, 1, true, 80);
    lv_chart_set_axis_tick(chart_tvoc, LV_CHART_AXIS_PRIMARY_X, 0, 0, 10, 1, true, 50);
    lv_chart_set_div_line_count(chart_tvoc, 0, 22);
    lv_chart_set_point_count(chart_tvoc, 22);
    lv_obj_add_event_cb(chart_tvoc, chart_tvoc_event_cb, LV_EVENT_ALL, NULL);
    lv_chart_set_zoom_x(chart_tvoc, 256 * 1);

    //lv_chart_set_range(chart_tvoc, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
    lv_chart_set_update_mode(chart_tvoc, LV_CHART_UPDATE_MODE_SHIFT);
    lv_obj_set_style_border_side(chart_tvoc, LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_BOTTOM, 0);
    lv_obj_set_style_radius(chart_tvoc, 0, 0);

    ser1_tvoc = lv_chart_add_series(chart_tvoc, lv_theme_get_color_primary(chart_tvoc), LV_CHART_AXIS_PRIMARY_Y);
    for (int i = 0; i < 22; i++) {
        lv_chart_set_next_value(chart_tvoc, ser1_tvoc, 0); //lv_rand(10, 80)
    }

    lv_obj_t *chart_co2_obj = lv_obj_create(parent);
    lv_obj_add_flag(chart_co2_obj, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
    lv_obj_set_flex_grow(chart_co2_obj, 1);
    lv_obj_set_grid_dsc_array(chart_co2_obj, grid_chart_col_dsc, grid_chart_row_dsc);

    lv_obj_set_height(chart_co2_obj, LV_PCT(100));
    lv_obj_set_style_max_height(chart_co2_obj, 300, 0);

    lv_obj_t *title_co2 = lv_label_create(chart_co2_obj);
    lv_label_set_text(title_co2, "CO2 Data");
    lv_obj_add_style(title_co2, &style_title, 0);
    lv_obj_set_grid_cell(title_co2, LV_GRID_ALIGN_START, 0, 2, LV_GRID_ALIGN_START, 0, 1);

    chart_co2 = lv_chart_create(chart_co2_obj);
    lv_group_add_obj(lv_group_get_default(), chart_co2);
    lv_obj_add_flag(chart_co2, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
    lv_obj_set_grid_cell(chart_co2, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_STRETCH, 1, 1);
    lv_chart_set_axis_tick(chart_co2, LV_CHART_AXIS_PRIMARY_Y, 0, 0, 5, 1, true, 80);
    lv_chart_set_axis_tick(chart_co2, LV_CHART_AXIS_PRIMARY_X, 0, 0, 10, 1, true, 50);
    lv_chart_set_div_line_count(chart_co2, 0, 22);
    lv_chart_set_point_count(chart_co2, 22);
    lv_obj_add_event_cb(chart_co2, chart_tvoc_event_cb, LV_EVENT_ALL, NULL);
    lv_chart_set_zoom_x(chart_co2, 256 * 1);

    lv_chart_set_update_mode(chart_co2, LV_CHART_UPDATE_MODE_SHIFT);
    lv_obj_set_style_border_side(chart_co2, LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_BOTTOM, 0);
    lv_obj_set_style_radius(chart_co2, 0, 0);

    ser1_co2 = lv_chart_add_series(chart_co2, lv_theme_get_color_primary(chart_co2), LV_CHART_AXIS_PRIMARY_Y);
    for (int i = 0; i < 22; i++) {
        lv_chart_set_next_value(chart_co2, ser1_co2, 0); //lv_rand(10, 80)
    }

    lv_obj_t *chart_temp_obj = lv_obj_create(parent);
    lv_obj_add_flag(chart_temp_obj, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
    lv_obj_set_flex_grow(chart_temp_obj, 1);
    lv_obj_set_grid_dsc_array(chart_temp_obj, grid_chart_col_dsc, grid_chart_row_dsc);

    lv_obj_set_height(chart_temp_obj, LV_PCT(100));
    lv_obj_set_style_max_height(chart_temp_obj, 300, 0);

    lv_obj_t *title_temp = lv_label_create(chart_temp_obj);
    lv_label_set_text(title_temp, "TEMP Data");
    lv_obj_add_style(title_temp, &style_title, 0);
    lv_obj_set_grid_cell(title_temp, LV_GRID_ALIGN_START, 0, 2, LV_GRID_ALIGN_START, 0, 1);

    chart_temp = lv_chart_create(chart_temp_obj);
    lv_group_add_obj(lv_group_get_default(), chart_temp);
    lv_obj_add_flag(chart_temp, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
    lv_obj_set_grid_cell(chart_temp, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_STRETCH, 1, 1);
    lv_chart_set_axis_tick(chart_temp, LV_CHART_AXIS_PRIMARY_Y, 0, 0, 5, 1, true, 80);
    lv_chart_set_axis_tick(chart_temp, LV_CHART_AXIS_PRIMARY_X, 0, 0, 10, 1, true, 50);
    lv_chart_set_div_line_count(chart_temp, 0, 22);
    lv_chart_set_point_count(chart_temp, 22);
    lv_obj_add_event_cb(chart_temp, chart_tvoc_event_cb, LV_EVENT_ALL, NULL);
    lv_chart_set_zoom_x(chart_temp, 256 * 1);

    lv_chart_set_update_mode(chart_temp, LV_CHART_UPDATE_MODE_SHIFT);
    lv_obj_set_style_border_side(chart_temp, LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_BOTTOM, 0);
    lv_obj_set_style_radius(chart_temp, 0, 0);
    lv_chart_set_range(chart_temp, LV_CHART_AXIS_PRIMARY_Y, 0, 50);
    ser1_temp = lv_chart_add_series(chart_temp, lv_theme_get_color_primary(chart_temp), LV_CHART_AXIS_PRIMARY_Y);
    for (int i = 0; i < 22; i++) {
        lv_chart_set_next_value(chart_temp, ser1_temp, 0); //lv_rand(10, 80)
    }

    lv_obj_t *chart_hum_obj = lv_obj_create(parent);
    lv_obj_add_flag(chart_hum_obj, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
    lv_obj_set_flex_grow(chart_hum_obj, 1);
    lv_obj_set_grid_dsc_array(chart_hum_obj, grid_chart_col_dsc, grid_chart_row_dsc);

    lv_obj_set_height(chart_hum_obj, LV_PCT(100));
    lv_obj_set_style_max_height(chart_hum_obj, 300, 0);

    lv_obj_t *title_hum = lv_label_create(chart_hum_obj);
    lv_label_set_text(title_hum, "HUMI Data");
    lv_obj_add_style(title_hum, &style_title, 0);
    lv_obj_set_grid_cell(title_hum, LV_GRID_ALIGN_START, 0, 2, LV_GRID_ALIGN_START, 0, 1);

    chart_hum = lv_chart_create(chart_hum_obj);
    lv_group_add_obj(lv_group_get_default(), chart_hum);
    lv_obj_add_flag(chart_hum, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
    lv_obj_set_grid_cell(chart_hum, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_STRETCH, 1, 1);
    lv_chart_set_axis_tick(chart_hum, LV_CHART_AXIS_PRIMARY_Y, 0, 0, 5, 1, true, 80);
    lv_chart_set_axis_tick(chart_hum, LV_CHART_AXIS_PRIMARY_X, 0, 0, 10, 1, true, 50);
    lv_chart_set_div_line_count(chart_hum, 0, 22);
    lv_chart_set_point_count(chart_hum, 22);
    lv_obj_add_event_cb(chart_hum, chart_tvoc_event_cb, LV_EVENT_ALL, NULL);
    lv_chart_set_zoom_x(chart_hum, 256 * 1);

    lv_chart_set_update_mode(chart_hum, LV_CHART_UPDATE_MODE_SHIFT);
    lv_obj_set_style_border_side(chart_hum, LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_BOTTOM, 0);
    lv_obj_set_style_radius(chart_hum, 0, 0);
    lv_chart_set_range(chart_temp, LV_CHART_AXIS_PRIMARY_Y, 0, 80);
    ser1_hum = lv_chart_add_series(chart_hum, lv_theme_get_color_primary(chart_hum), LV_CHART_AXIS_PRIMARY_Y);
    for (int i = 0; i < 22; i++) {
        lv_chart_set_next_value(chart_hum, ser1_hum, 0); //lv_rand(10, 80)
    }
}

// 创建信息界面
static void info_create(lv_obj_t *parent) {
    lv_obj_t *info_obj = lv_obj_create(parent);
    lv_obj_set_height(info_obj, LV_SIZE_CONTENT);

    lv_obj_t *info_label = lv_label_create(info_obj);
    lv_label_set_text(info_label, "Environment Sensor");
    lv_obj_add_style(info_label, &style_text, 0);

    lv_obj_t *designed_label = lv_label_create(info_obj);
    lv_label_set_text(designed_label, "Designed by WangDa");
    lv_obj_add_style(designed_label, &style_text, 0);

    lv_obj_t *email_icn = lv_label_create(info_obj);
    lv_obj_add_style(email_icn, &style_icon, 0);
    lv_label_set_text(email_icn, LV_SYMBOL_ENVELOPE);

    lv_obj_t *email_label = lv_label_create(info_obj);
    lv_obj_add_style(email_label, &style_text_muted, 0);
    lv_label_set_text(email_label, "wangda888@outlook.com");
    lv_label_set_long_mode(email_label, LV_LABEL_LONG_SCROLL_CIRCULAR);

    static lv_coord_t grid_main_col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t grid_main_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(parent, grid_main_col_dsc, grid_main_row_dsc);

    static lv_coord_t grid_info_col_dsc[] = {LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static lv_coord_t grid_info_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(info_obj, grid_info_col_dsc, grid_info_row_dsc);

    lv_obj_set_grid_cell(info_obj, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(info_label, LV_GRID_ALIGN_CENTER, 0, 2, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(designed_label, LV_GRID_ALIGN_CENTER, 0, 2, LV_GRID_ALIGN_CENTER, 1, 1);
    lv_obj_set_grid_cell(email_icn, LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 2, 1);
    lv_obj_set_grid_cell(email_label, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_CENTER, 2, 1);
}

// 显示界面初始化
int lv_disp_interface(void) {
    lv_coord_t tab_h = 45;
    font_large = LV_FONT_DEFAULT;
    font_title = &lv_font_montserrat_20;
    font_normal = LV_FONT_DEFAULT;

#if LV_USE_THEME_DEFAULT
    lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK, font_normal);
#endif

    lv_style_init(&style_text_muted);
    lv_style_set_text_opa(&style_text_muted, LV_OPA_50);

    lv_style_init(&style_title);
    lv_style_set_text_font(&style_title, font_title);

    lv_style_init(&style_text);
    lv_style_set_text_font(&style_text, font_normal);

    lv_style_init(&style_icon);
    lv_style_set_text_color(&style_icon, lv_theme_get_color_primary(NULL));
    lv_style_set_text_font(&style_icon, font_large);

    lv_style_init(&style_bullet);
    lv_style_set_border_width(&style_bullet, 0);
    lv_style_set_radius(&style_bullet, LV_RADIUS_CIRCLE);

    lv_style_init(&style_column_small);
    lv_style_set_pad_column(&style_column_small, 0);

    tv = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, tab_h);
    lv_obj_set_style_text_font(lv_scr_act(), font_normal, 0);

    lv_obj_t *t1 = lv_tabview_add_tab(tv, "Home");
    lv_obj_t *t2 = lv_tabview_add_tab(tv, "History");
    lv_obj_t *t3 = lv_tabview_add_tab(tv, "Info");

    home_create(t1);
    history_create(t2);
    info_create(t3);

    return 0;
}

// 基线回调函数
void bl_callback() {
    if (!sgp.getBaseline()) {
        preferences.putUInt("co2", sgp.baselineCO2);
        preferences.putUInt("tvoc", sgp.baselineTVOC);
    }
    Serial.println(sgp.baselineCO2);
    Serial.print(sgp.baselineTVOC);
    //Serial.println("bl_callback");
}

void update_chart_callback() {
  static uint16_t max_value_tvoc = 400,max_value_co2 = 400, max_value_temp = 0, max_value_hum = 0, count = 0;
  if (count >= 22) {
    max_value_tvoc = 400;
    count = 0;
  }
  if (sgp.TVOC > max_value_tvoc) {
    max_value_tvoc = sgp.TVOC;
    lv_chart_set_range(chart_tvoc, LV_CHART_AXIS_PRIMARY_Y, 0, max_value_tvoc);
  }

  if (sgp.CO2 > max_value_co2) {
    max_value_co2 = sgp.CO2;
    lv_chart_set_range(chart_co2, LV_CHART_AXIS_PRIMARY_Y, 0, max_value_co2);
  }

  if (sht_temp > max_value_temp) {
    max_value_temp = sht_temp;
    lv_chart_set_range(chart_tvoc, LV_CHART_AXIS_PRIMARY_Y, 0, max_value_temp);
  }

  if (sht_hum > max_value_hum) {
    max_value_tvoc = sht_hum;
    lv_chart_set_range(chart_tvoc, LV_CHART_AXIS_PRIMARY_Y, 0, max_value_hum);
  }

  lv_chart_set_next_value(chart_tvoc, ser1_tvoc, sgp.TVOC);
  lv_chart_set_next_value(chart_tvoc, ser1_co2, sgp.CO2);
  lv_chart_set_next_value(chart_tvoc, ser1_temp, sht_temp);
  lv_chart_set_next_value(chart_tvoc, ser1_hum, sht_hum);
  //Serial.println("update_chart_callback");
}

// 初始化函数
void setup() {
    Serial.begin(115200); // 准备串口调试
    Wire.setPins(5, 4);
    Wire.begin();
    String LVGL_Arduino = "Hello Arduino! ";
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    Serial.println(LVGL_Arduino);

    if (sht.init()) {
        Serial.print("init(): success\n");
    } else {
        Serial.print("init(): failed\n");
    }
    sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // 仅支持SHT3x

    if (sht.readSample()) {
        Serial.print("SHT:\n");
        Serial.print("  RH: ");
        Serial.print(sht.getHumidity(), 2);
        Serial.print("\n");
        Serial.print("  T:  ");
        Serial.print(sht.getTemperature(), 2);
        Serial.print("\n");
    } else {
        Serial.print("Error in readSample()\n");
    }

    if (sgp.begin() == false) {
        Serial.println("No SGP30 Detected. Check connections.");
        while (1);
    }
    sgp.initAirQuality();
    sgp.measureAirQuality();
    Serial.print("CO2: ");
    Serial.print(sgp.CO2);
    Serial.print(" ppm\tTVOC: ");
    Serial.print(sgp.TVOC);
    Serial.println(" ppb");

    // 设置引脚模式
    pinMode(BEE_PIN, OUTPUT);
    digitalWrite(BEE_PIN, LOW);

    pinMode(FAN_PIN, OUTPUT);
    digitalWrite(FAN_PIN, LOW);

    touch.begin();

    // 初始化LVGL
    lv_init();

    // 初始化TFT显示
    tft.begin();
    tft.setRotation(1);

    // 设置触摸屏校准数据
    // uint16_t calData[5] = { 275, 3620, 264, 3532, 1 };
    // tft.setTouch(calData);

    lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * screenHeight / 10);

    // 初始化显示驱动
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    // 初始化输入设备驱动
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read;
    lv_indev_drv_register(&indev_drv);

    // 创建简单标签
    // lv_obj_t *label = lv_label_create(lv_scr_act());
    // lv_label_set_text(label, "Hello Arduino and LVGL!");
    // lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);

    // 初始化显示界面
     /*Or try out a demo. Don't forget to enable the demos in lv_conf.h. E.g. LV_USE_DEMOS_WIDGETS*/
      //lv_demo_widgets();
      lv_disp_interface();
     //lv_demo_benchmark();
     //lv_demo_keypad_encoder();
     //lv_demo_music();
     //lv_demo_printer();
    // lv_demo_stress();

    // 加载SGP30基线
    preferences.begin("sgp_baseline");
    uint16_t bl_co2 = preferences.getUInt("co2", 0);
    uint16_t bl_tvoc = preferences.getUInt("tvoc", 0);
    if (bl_co2 && bl_tvoc) {
        sgp.setBaseline(bl_co2, bl_tvoc);
        Serial.print("CO2 baseline: ");
        Serial.println(bl_co2);
        Serial.print("TVOC baseline: ");
        Serial.println(bl_tvoc);
        Serial.println("SGP30 Baseline set done");
    }
    bl_ticker.attach(3600, bl_callback);
    update_chart_ticker.attach(3600, update_chart_callback);
    Serial.println("Setup done");
    digitalWrite(BEE_PIN, 1);
    delay(100);
    digitalWrite(BEE_PIN, 0);
    //digitalWrite(FAN_PIN, 1);
    ledcSetup(FAN_PIN, 20000, 8);
    ledcAttachPin(FAN_PIN, FAN_PIN);
    ledcWrite(FAN_PIN, 128);
}

void loop() {
    lv_timer_handler(); // 处理LVGL的定时任务
    delay(5);
}
